<cfcomponent output="false">
	
	<!--- 
	#############################################################################
	STATIC PROPERTIES
	#############################################################################--->
	<cfset this.configFile = ExpandPath("/config/mof.settings.ini.cfm") />
	<cfset this.sessionManagement = true />
	
	<!--- 
	#############################################################################
	TOGGLE DEBUG DECORATOR
	#############################################################################--->
	<cfset this.enableDebug = GetProfileString(this.configFile,"properties","enableDebug") />
	
	<cfif NOT StructKeyExists(request,"decoratorenabled") AND Len(this.enableDebug) AND this.enableDebug>
		<cfset request.decoratorenabled = true />
		<cfset this = CreateObject("component","mof.CoreDebugDecorator").init() />
	</cfif>	
	
		
	<!--- 
	#############################################################################
	INITIALIZATION METHODS
	#############################################################################--->
	<cffunction name="runFramework" access="public" output="true" returntype="void">
		<cfset var startTime = getTickCount() />
		<cfif NOT StructKeyExists(application,"MOF_LOADED") OR NOT application.MOF_LOADED OR application[this.getAppKey()]["configFileLastModified"] NEQ getConfigFileModifiedDate()>
			<cfset this.loadFramework() />
			<cflock type="exclusive" name="#application.ApplicationName#_MOF_LOADED" timeout="30">
				<cfif NOT StructKeyExists(application,"MOF_LOADED") OR NOT application.MOF_LOADED>
					<cfset application.MOF_LOADED = true />
				</cfif>
			</cflock>
		</cfif>
		<cfset this.parseUrl() />
		<cfset this.runMof() />
		<cfreturn />
	</cffunction>
	
	<cffunction name="reloadFramework" access="public" output="false" returntype="void">
		<cfif IsDefined("application")>
			<cflock type="exclusive" name="#application.ApplicationName#_MOF_LOADED" timeout="30">
				<cfset application.MOF_LOADED = false /> 
			</cflock>			
		</cfif>	
	</cffunction>
	
	<cffunction name="loadFramework" access="public" output="false" returntype="void">
		<cfset var i = "" />
		<cfset this.setAppKey() />
		
		<cflock type="exclusive" name="#application.ApplicationName#_mof_initialization" timeout="30">
			<cfloop collection="#application#" item="i">
				<cfif i NEQ this.getAppKey() AND Left(i,Len("mof_app_key_" & application.ApplicationName)) EQ "mof_app_key_" & application.ApplicationName>
					<cfset StructDelete(application,i) />
				</cfif>
			</cfloop>
			<cfset application[this.getAppKey()]["configFileLastModified"] = this.getConfigFileModifiedDate() />
			<cfset application[this.getAppKey()]["properties"] = StructNew() />		
			<cfset application[this.getAppKey()]["controllers"] = StructNew() />	
		</cflock>		
		
		<cfset this.loadProperties() />
		<cfset this.loadServiceFactory() />
		<cfset this.loadPlugins() />	
		<cfset this.loadSubroutines() />
	</cffunction>

	<cffunction name="getConfigFileModifiedDate" access="public" output="false" returntype="date">
		<cfreturn CreateObject("java","java.io.File").init(this.configFile).lastModified() />
	</cffunction>

	<cffunction name="setAppKey" access="public" output="false" returntype="void">
		<cflock type="exclusive" name="#application.ApplicationName#_mof_initialization" timeout="30">
			<cfset application.MOF_FRAMEWORK.appkey = "mof_app_key_" & application.ApplicationName & GetTickCount() />			
		</cflock>
		<cfreturn />
	</cffunction>
	
	<cffunction name="getAppKey" access="public" output="false" returntype="string">
		<cfreturn application.MOF_FRAMEWORK.appkey />
	</cffunction>

	<cffunction name="loadProperties" access="public" output="false" returntype="void">
		<cfset var settingsFile = getProfileSections(this.configFile) />
		<cfset var propertiesList = "" />
		<cfset var i = "" />
		
		
		<!--- INITIALIZE PROPERTIES STRUCT --->
		<cfset application[this.getAppKey()].properties = StructNew() />
		
		<!--- LOAD PROPERTIES FROM THE mod.settings.ini.cfm FILE --->
		<cftry>
			<cfset propertiesList = settingsFile["properties"] />
			<cfloop list="#propertiesList#" index="i">
				<cfset this.setProperty(i,getProfileString(this.configFile,"properties",i)) />
			</cfloop>
			<cfcatch>
				<cfthrow message="There was an error loading the [properties] section of the file #this.configFile#" />
			</cfcatch>
		</cftry>
	</cffunction>
	
	<cffunction name="setProperty" access="public" output="false" returntype="void">
		<cfargument name="name" type="string" required="true" />
		<cfargument name="value" type="any" required="true" />
		<cflock type="exclusive" name="#application.ApplicationName#_property" timeout="5">
			<cfset application[this.getAppKey()].properties[arguments.name] = arguments.value />
		</cflock>		
		<cfreturn />
	</cffunction>
	
	<cffunction name="getProperty" access="public" output="false" returntype="any">
		<cfargument name="name" type="string" required="true" />
		<cfif StructKeyExists(application[this.getAppKey()].properties,arguments.name)>
			<cfreturn application[this.getAppKey()].properties[arguments.name] />
		<cfelse>
			<cfreturn "" />
		</cfif>		
	</cffunction>
	
	<cffunction name="getProperties" access="public" output="false" returntype="struct">
		<cfreturn application[this.getAppKey()].properties />
	</cffunction>
	
	<cffunction name="loadPlugins" access="public" output="false" returntype="void">
		<cfset var plugins = "" />
		<cfset var thisPlugin = "" />
		<cfset application[this.getAppKey()]["plugins"] = ArrayNew(1) />
		
		<cfdirectory action="list" directory="#ExpandPath("/plugins")#" name="plugins" filter="*.cfc" />
		
		<cfloop query="plugins">
			<cfset thisPlugin = ListDeleteAt(plugins.name,ListLen(plugins.name,"."),".") />
			<cfif thisPlugin NEQ "Application" AND Len(plugins.name) GT 4>
				<cftry>
				<cfset ArrayAppend(application[this.getAppKey()]["plugins"],CreateObject("component","plugins.#thisPlugin#").init()) />				
				<cfcatch>
					<cfthrow message="Error trying to load the plugin #thisPlugin#" /></cfcatch>
				</cftry>			
			</cfif>
		</cfloop>
		<cfreturn />
	</cffunction>	
		
	<cffunction name="loadSubroutines" access="public" output="false" returntype="void">
		<cfset var subroutines = "" />
		<cfset var thisSubroutine = "" />
		<cfset var i = "" />
		
		<cfset application[this.getAppKey()]["subroutines"] = StructNew() />	
				
		<cfdirectory action="list" directory="#ExpandPath("/subroutines")#" name="subroutines" filter="*.cfc" />
				
		<cfloop query="subroutines">
			<cfset thisSubroutine = ListDeleteAt(subroutines.name,ListLen(subroutines.name,"."),".") />
			<cfif thisSubroutine NEQ "Application" AND Len(subroutines.name) GT 4>
				<cftry>
					<cfset application[this.getAppKey()]["subroutines"][Left(subroutines.name,Len(subroutines.name)-4)] = CreateObject("component","subroutines.#thisSubroutine#").init() />				
					<cfcatch>
						<cfthrow message="Error trying to load the subroutine #thisSubroutine#" />
					</cfcatch>
				</cftry>			
			</cfif>
		</cfloop>		
		
		<cfreturn />	
	</cffunction>
	
	<cffunction name="getPlugins" access="public" output="false" returntype="array">
		<cfreturn application[this.getAppKey()]["plugins"] />	
	</cffunction>
	
	<cffunction name="getSubroutines" access="public" output="false" returntype="struct">
		<cfreturn application[this.getAppKey()]["subroutines"] />	
	</cffunction>

	<cffunction name="loadServiceFactory" access="public" output="false" returntype="void">
		<cfset var settingsFile = GetProfileSections(this.configFile) />
		<cfset var serviceFactoryKeys = "" />
		<cfset var properties = StructNew() />
		<cfset var i = "" />
			
		<cfif StructKeyExists(settingsFile,"servicefactory") AND ListFindNoCase(settingsFile["servicefactory"],"framework")>
			<cfloop list="#settingsFile["servicefactory"]#" index="i">
				<cfif REFindNoCase("^\${.*}",GetProfileString(this.configFile,"servicefactory",i))>
					<cfset properties[i] = getProperty(REReplaceNoCase(REReplaceNoCase(GetProfileString(this.configFile,"servicefactory",i),"^\${",""),"}$","")) />
				<cfelse>
					<cfset properties[i] = GetProfileString(this.configFile,"servicefactory",i) />
				</cfif>
			</cfloop>
			<cftry>
				<cfset properties["framework"] = UCase(Left(properties["framework"],1))&Right(LCase(properties["framework"]),Len(properties["framework"])-1) />
				<cfset this.setProperty("serviceFactory",CreateObject("component","mof.servicefactory.Load#properties["framework"]#").init(argumentCollection=properties).loadServiceFactory()) />
				<cfcatch>
					<cfdump var="#cfcatch#" /><cfabort>
				</cfcatch>
			</cftry>			
		</cfif>
		<cfreturn />
	</cffunction>
	
	<!--- 
	#############################################################################
	REQUEST LEVEL METHODS
	#############################################################################--->	
	<cffunction name="parseUrl" access="public" output="false">
		<cfset var i = "" />
		<cfset var list = cgi.PATH_INFO />
		<cfset var urlParameters = "" />
		<cfset var thisArg = "" />
		<cfset var thisValue = "" />
		
		<cfset request[this.getAppKey()]["controller"] = this.getProperty("defaultController") />
		<cfset request[this.getAppKey()]["handler"] = "" />
		<cfset request[this.getAppKey()].args = StructNew() />
				
		<cfswitch expression="#ListLen(list,"/")#">
			<cfcase value="0">
				<!--- this is the main app root. --->
			</cfcase>
			<cfcase value="1">
				<!--- This would be the default handler in the #ListFirst(list)# controller --->
				<cfset request[this.getAppKey()]["controller"] = ListFirst(list,"/") />
			</cfcase>
			<cfdefaultcase>
				<cfset request[this.getAppKey()]["controller"] = ListFirst(list,"/") />
				<cfset request[this.getAppKey()]["handler"] = ListGetAt(list,2,"/") />
				
				<cfset urlParameters = ListDeleteAt(ListDeleteAt(list,1,"/"),1,"/") />
				
				<cfloop condition="#Len(urlParameters)#">
					<cfset thisArg = ListFirst(urlParameters,"/") />
					<cfset thisValue = "" />
					<cfset urlParameters = ListDeleteAt(urlParameters,1,"/") />
					<cfif ListLen(urlParameters,"/")>
						<cfset thisValue = UrlDecode(ListFirst(urlParameters,"/")) />
						<cfset urlParameters = ListDeleteAt(urlParameters,1,"/") />
					</cfif>
					<cfset url[thisArg] = thisValue />
				</cfloop>		
				
				<cfset StructAppend(request[this.getAppKey()].args,url)>
				<cfset StructAppend(request[this.getAppKey()].args,form)>				
			</cfdefaultcase>
		</cfswitch>		
	</cffunction>
	
	<cffunction name="runMof" access="public" output="true" returntype="void">
		<cfset var controller = this.getController(request[this.getAppKey()]["controller"]) />
		<cfset var view = "" />
		<cfset var handler = "" />
	
		<cfset this.runPreEventPlugins() />
		
		<cfset this.initializeSession() />
		<cfset this.injectPersistArgs() />	
		<cfset this.initializeLayout() />		
		
		<cftry>
			<cfif Len(request[this.getAppKey()]["handler"])>
				<cfset handler = request[this.getAppKey()]["handler"] />
			<cfelse>
				<cfset handler = controller.defaultHandler />
			</cfif>
			<cfset Evaluate("controller.#handler#()") />
			<cfcatch>
				<!--- <cfthrow message="Problem loading handler #handler#().  #cfcatch.Message#.  #cfcatch.Detail#" /> --->
			</cfcatch>
		</cftry>			

		<cfset this.appendViewStack(this.buildView(request[this.getAppKey()]["controller"]&"."&handler)) />		
		
		<cfif this.isArgDefined("dumpargs")>
			<cfdump var="#this.getProperties()#" />
			<cfdump var="#this.getArgs()#" />
			<cfdump var="controller: #request[this.getAppKey()]["controller"]#" />
			<cfdump var="handler: #request[this.getAppKey()]["handler"]#" />		
			<cfabort/>
		</cfif>
		
		<cfset this.doLayout() />
		
		<cfset this.runPostEventPlugins() />
		
		<cfreturn />
	</cffunction>	
	
	
	<cffunction name="getController" access="public" output="false" returntype="mof.AbstractController">
		<cfargument name="controller" type="string" required="true" />
		<cfset var list = "" />
		<cfset var thisFile = "" />
		<cfset var isLoaded = false />
		<cfif NOT StructKeyExists(application[this.getAppKey()]["controllers"],arguments.controller)>
			<cfdirectory action="list" directory="#ExpandPath("/controllers")#" name="list" filter="*.cfc">
			<cfif list.recordcount>
				<cfloop query="list">
					<cfset thisFile = ListDeleteAt(list.name,ListLen(list.name,"."),".") />
					
					<cfif thisFile EQ arguments.controller>
						<cftry>
							<cfset application[this.getAppKey()]["controllers"][arguments.controller] = CreateObject("component","controllers.#thisFile#").init() />
							<cfcatch>
								<cfthrow message="There was an error loading the controller #arguments.controller#. Exception: #cfcatch.Message#.  Detail: #cfcatch.Detail#" />
							</cfcatch>
						</cftry>
						<cfset isLoaded = true />
						<cfbreak />
					</cfif>					
				</cfloop>
			<cfelse>
				<cfthrow message="No controllers exist in /controllers" />
			</cfif>
		</cfif>
		
		<cfif StructKeyExists(application[this.getAppKey()]["controllers"],arguments.controller)>
			<cfreturn application[this.getAppKey()]["controllers"][arguments.controller] />
		<cfelse>
			<cfthrow message="The controller /controllers/#arguments.controller#.cfc is not available." />
		</cfif>		
	</cffunction>	
	
	<cffunction name="initializeSession" access="public" output="false" returntype="void">
		<cfif NOT StructKeyExists(session,this.getAppKey())>
			<cfset session[this.getAppKey()] = StructNew() />
		</cfif>
		<cfreturn />
	</cffunction>	
	
	<cffunction name="runPreEventPlugins" access="public" output="false" returntype="void">
		<cfset var plugins = this.getPlugins() />
		<cfset var i = "" />
		<cfif ArrayLen(plugins)>
			<cfloop from="1" to="#ArrayLen(plugins)#" index="i">
				<cfset plugins[i].preEvent() />
			</cfloop>
		</cfif>
		<cfreturn />
	</cffunction>

	<cffunction name="runPostEventPlugins" access="public" output="false" returntype="void">
		<cfset var plugins = this.getPlugins() />
		<cfset var i = "" />
		<cfif ArrayLen(plugins)>
			<cfloop from="1" to="#ArrayLen(plugins)#" index="i">
				<cfset plugins[i].postEvent() />
			</cfloop>
		</cfif>
		<cfreturn />
	</cffunction>
	
	<cffunction name="execute" access="public" output="false" returntype="void">
		<cfargument name="subroutine" type="string" required="true" />
		<cfset var subroutines = getSubroutines() />
		<cfif StructKeyExists(subroutines,arguments.subroutine)>
			<cfset subroutines[arguments.subroutine].execute() />
		<cfelse>
			<cfthrow message="The subroutine #arguments.subroutine#.cfc does not exist in /subroutines" />
		</cfif>
		<cfreturn />
	</cffunction>
	
	<!--- 
	#############################################################################
	METHODS RELATED TO EVENT ARGS
	#############################################################################--->
	<cffunction name="setArg" access="public" output="false" returntype="void">
		<cfargument name="name" type="string" required="true" />
		<cfargument name="value" type="any" required="false" default="" />
		<cfset request[this.getAppKey()]["args"][arguments.name] = arguments.value />
		<cfreturn />
	</cffunction>
	
	<cffunction name="getArg" access="public" output="false" returntype="any">
		<cfargument name="name" type="string" required="true" />
		<cfargument name="defaultValue" type="any" required="false" default="" />
		<cfif StructKeyExists(request[this.getAppKey()]["args"],arguments.name)>
			<cfreturn request[this.getAppKey()]["args"][arguments.name] />
		<cfelse>
			<cfreturn arguments.defaultValue />
		</cfif>	
	</cffunction>
	
	<cffunction name="getArgs" access="public" output="false" returntype="struct">
		<cfreturn request[this.getAppKey()].args />
	</cffunction>

	<cffunction name="isArgDefined" access="public" output="false" returntype="boolean">
		<cfargument name="name" type="string" required="true" />
		<cfreturn StructKeyExists(request[this.getAppKey()].args,arguments.name)>
	</cffunction>


	
	<!--- 
	#############################################################################
	VIEW STACK METHODS
	#############################################################################--->
	<cffunction name="initViewStack" access="public" output="false" returntype="void">
		<cfargument name="stack" type="string" required="false" default="#this.getAppKey()#_body_content">
		<cfif NOT StructKeyExists(request[this.getAppKey()],"viewstack")>
			<cfset request[this.getAppKey()]["viewStack"] = StructNew() />
		</cfif>
		<cfif NOT StructKeyExists(request[this.getAppKey()]["viewStack"],arguments.stack)>
			<cfset request[this.getAppKey()]["viewStack"][arguments.stack] = "" />	
		</cfif>
		<cfreturn />
	</cffunction>
	
	<cffunction name="appendViewStack" access="public" output="false" returntype="void">
		<cfargument name="content" type="string" required="true" />
		<cfargument name="stack" type="string" required="false" default="#this.getAppKey()#_body_content">
		<cfset this.initViewStack(arguments.stack) />
		<cfset request[this.getAppKey()]["viewStack"][arguments.stack] = request[this.getAppKey()]["viewStack"][arguments.stack] & arguments.content />
	</cffunction>
	
	<cffunction name="getViewStack" access="private" output="false" returntype="string">
		<cfargument name="stack" type="string" required="false" default="#this.getAppKey()#_body_content">
		<cfif StructKeyExists(request[this.getAppKey()]["viewStack"],arguments.stack)>
			<cfreturn request[this.getAppKey()]["viewStack"][arguments.stack] />	
		<cfelse>
			<cfreturn "" />
		</cfif>
	</cffunction>

	
	<!--- 
	#############################################################################
	VIEW METHODS
	#############################################################################--->
	<cffunction name="buildView" access="public" output="false" returntype="string" hint="I load a view and return its contents">
		<cfargument name="view" type="string" required="true" />
		<cfset var content = "" />
		<cfset var viewList = "" />
		
		<cfdirectory action="list" directory="#ExpandPath("/views/#ListFirst(arguments.view,".")#")#" filter="*.cfm" name="viewList" />
		<cfloop query="viewList">
			<cfif viewList.name EQ ListLast(arguments.view,".")&".cfm">			
				<cfsavecontent variable="content"><cfinclude template="/views/#LCase(ListFirst(arguments.view,"."))#/#viewList.name#"></cfsavecontent>
				<cfbreak />
			</cfif>
		</cfloop>	
		<cfreturn content />
	</cffunction>
	
	
	<!---
	#################################################################################
	LAYOUT FUNCTIONS
	##################################################################################--->
	<cffunction name="initializeLayout" access="public" output="false" returntype="void">
		<cfset this.setLayout(this.getProperty("defaultLayout")) />
		<cfreturn />
	</cffunction>
	
	<cffunction name="setLayout" access="public" output="false" returntype="void">
		<cfargument name="layout" type="string" required="true" />
		<cfset request[this.getAppKey()]["layout"] = arguments.layout />
		<cfreturn />
	</cffunction>
	
	<cffunction name="getLayout" access="public" output="false" returntype="string">
		<cfreturn request[this.getAppKey()]["layout"] />
	</cffunction>
	
	<cffunction name="doLayout" access="public" output="true" returntype="void">
		<cfif Len(this.getLayout())>
			<cfinclude template="/views/#ListChangeDelims(this.getLayout(),"/",".")#.cfm" />
		</cfif>
		<cfreturn />
	</cffunction>
	
	
	
	<!--- 
	#############################################################################
	REDIRECT FUNCTIONALITY
	#############################################################################--->
	<cffunction name="setPersistArgs" access="public" output="false" returntype="void">
		<cfargument name="args" type="struct" required="true" />
		<cfset session[this.getAppKey()]["_persistArgs"] = args />
	</cffunction>
	
	<cffunction name="getPersistArgs" access="public" output="false" returntype="struct">
		<cfset var args = StructNew() />
		<cfif StructKeyExists(session[this.getAppKey()],"_persistArgs") AND IsStruct(session[this.getAppKey()]["_persistArgs"]) AND NOT StructIsEmpty(session[this.getAppKey()]["_persistArgs"])>
			<cfset StructAppend(args,session[this.getAppKey()]["_persistArgs"]) />
			<cfset StructDelete(session[this.getAppKey()],"_persistArgs") />			
		</cfif>
		<cfreturn args />
	</cffunction>
	
	<cffunction name="injectPersistArgs" access="public" output="false" returntype="void">
		<cfset var args = this.getPersistArgs() />
		<cfset var i = "" />		
		<!--- TODO: for some reason, StructAppend isn't working like I am expecting. Looping for now --->
		<cfloop collection="#args#" item="i">
			<cfset this.setArg(i,args[i]) />
		</cfloop>	
		<cfreturn />
	</cffunction>

	<cffunction name="populateBean" access="public" output="false" returntype="void">
		<cfargument name="bean" type="any" required="true" />
		<cfset var i = "" />	
		<cfloop collection="#getArgs()#" item="i">
			<cftry>				
				<cfset Evaluate("arguments.bean.set" & i & "(getArg(""" & i & """))") />
				<cfcatch><!--- this setter didn't exist in the target bean ---></cfcatch>
			</cftry>
		</cfloop>
	</cffunction>
	
	<cffunction name="redirect" access="public" output="false" returntype="void">
		<cfargument name="controller" type="string" required="true" />
		<cfargument name="handler" type="string" required="false" default="" />
		<cfargument name="persist" type="boolean" required="false" default="true" />
		<cfargument name="args" required="false" default="StructNew()" />
		
		<cfset var urlstring = "/index.cfm/#arguments.controller#/" />
		<cfset var i = "" />
		
		<cfif arguments.persist>
			<cfset this.setPersistArgs(this.getArgs()) />
		</cfif>
		
		<cfif Len(arguments.handler)>
			<cfset urlstring = urlstring & arguments.handler & "/" />
		</cfif>
		
		<cfif IsStruct(arguments.args) AND NOT StructIsEmpty(arguments.args)>
			<cfloop collection="#arguments.args#" item="i">
				<cfset urlstring = urlstring & i & "/" />
				<cfset urlstring = urlstring & arguments.args[i] & "/" />
			</cfloop>		
		</cfif>
		<cflocation url="#urlstring#" addtoken="false" />
	</cffunction>	

	<cffunction name="buildUrl" access="public" output="false" returntype="string">
		<cfargument name="controller" type="string" required="true" />
		<cfargument name="handler" type="string" required="false" default="" />
		<cfargument name="args" type="struct" required="false" default="#StructNew()#" />
		<cfset var urlstring = "/index.cfm/#arguments.controller#/" />
		<cfset var i = "" />
		<cfif Len(arguments.handler)>
			<cfset urlstring = urlstring & arguments.handler & "/" />
		</cfif>
		<cfloop collection="#args#" item="i">
			<cfset urlstring = urlstring & LCase(i) & "/" & arguments.args[i] & "/" />
		</cfloop>
		<cfreturn urlstring />
	</cffunction>
</cfcomponent>